package sketch.generator;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import sketch.util.Checker;

public abstract class Generator<T> implements IGenerator<T> {
	/**
	 * A self-increasing counter
	 * */
	private static int counter = 0; 
	
	/**
	 * A unique id for each generator
	 * */
	public final int ID;
	
	protected boolean isReset;
	protected T current = null;
	
	//steps in calling next()
	protected int step = 0;
	
	public int getStep() {
		return step;
	}
	
	public void moveForward(int step) {
		int currentStep = this.step;
		int needToMove = step;
		Checker.checkTrue(step > 0, "The step: " + currentStep + " to move could not <= 0.");
		while(step > 0) {
			next();
			step --;
		}
		//set the moved step
		Checker.checkTrue(this.step == currentStep + needToMove, "The step moved is not correct! This.step: "
				+ this.step + ", while current step + step is: " + (currentStep + needToMove));
	}
	
	protected Generator() {
		isReset = true;
		//set the unique ID of each generator
		ID = (counter++);
	}
	
	public void reset() {
		isReset = true;
		step = 0;
		current = null; //set the current to null
	}
	
	//get all the results, and reset the state
	//from the beginning
	public Object[] all() {
		//first, we need to reset the state
		reset();
		//the fetch all
		List<T> allItems = new LinkedList<T>();
		while(hasNext()) {
			allItems.add(next());
		}
		//convert to an array
		Object[] ret = new Object[allItems.size()];
		for(int i = 0; i < allItems.size(); i++) {
			ret[i] = allItems.get(i);
		}
		//reset here
		reset();
		return ret;
	}
	
	public T next() {
		//increase the number of step
		this.step ++;
		current = generateNext();
		isReset = false;
		return current;
	}
	
	public boolean isReset() {
		return isReset;
	}
	
	public T current() {
		return current;
	}	
	
	/**These two methods need to be overriden */
	public abstract boolean hasNext();
	protected abstract T generateNext();	

	public final Iterator<T> iterator() {
		return new ForLoopIterator<T>(this);
	}
}
